package com.zrf.base.knowledge.concurrent.demo;

import com.google.common.collect.Lists;
import org.apache.commons.lang3.RandomUtils;

import java.text.MessageFormat;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.StampedLock;

/**
 * @date 2020/3/21
 * @AUTHER zhouruifeng
 */
public class StampedLockDemo {

    final StampedLock lock = new StampedLock();

    Integer value = 1;

    public void setValue(Integer value) {
        long l = lock.writeLock();
        try {
            this.value = value;
            System.out.println(MessageFormat.format("set value: {0}, thread:{1}", value, Thread.currentThread().getName()));
        } finally {
            lock.unlockWrite(l);
        }
    }

    public Integer getValue() {
        long l = lock.tryOptimisticRead();
        Integer tmp = this.value;
        if (!lock.validate(l)) {
            l = lock.readLock();
            try {
                tmp = this.value;
            } finally {
                lock.unlockRead(l);
            }
        }
        System.out.println(MessageFormat.format("get value: {0}, thread:{1}", tmp, Thread.currentThread().getName()));
        return tmp;
    }

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(10);

        StampedLockDemo stampedLockDemo = new StampedLockDemo();

        List<CompletableFuture> list = Lists.newArrayList();
        for (int i = 0; i < 100; i++) {
            list.add(CompletableFuture.runAsync(() -> {
                if (RandomUtils.nextInt(0, 100) < 20) {
                    stampedLockDemo.setValue(RandomUtils.nextInt(0, 100));
                } else {
                    stampedLockDemo.getValue();
                }
            }));
        }
        list.forEach(future -> {
            try {
                future.get();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        });

        executorService.shutdown();
    }
}
